// 编译 gcc main_md5.c md5.c -Wall -o md5

#include "md5.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define READ_DATA_SIZE	1024
#define MD5_SIZE		16
#define MD5_STR_LEN		(MD5_SIZE * 2)

// function declare
int Compute_string_md5(unsigned char *dest_str, unsigned int dest_len, char *md5_str);
int Compute_file_md5(const char *file_path, char *md5_str);

int main(int argc, char *argv[])
{
	if(3 != argc)
	{
		return -1;
	}
	
	// 存储类型
	char type[7] = {};
	// 文件路径
	char file_path[1024] = {};
	// 计算的字符串
	char test_str[1024] = {};
	// 生成的md5串
	char md5_str[MD5_STR_LEN + 1] = {};
	// 存储函数调用返回值
	int ret = 0;
	
	strncpy(type, argv[1], 7);

	if(0 == strncmp(type, "file", 6))
	{
		strncpy(file_path, argv[2], 1024);
		// 生成 file md5
		ret = Compute_file_md5(file_path, md5_str);
		if (0 == ret)
		{
			//printf("[file - %s] md5 value:\n", file_path);
			printf("%s\n", md5_str);
		}
	}
	else if(0 == strncmp(type, "string", 6))
	{
		strncpy(test_str, argv[2], 1024);
		// 生成 string md5 
		Compute_string_md5((unsigned char *)test_str, strlen(test_str), md5_str);
		//printf("[string - %s] md5 value:\n", test_str);
		printf("%s\n", md5_str);
	}

	return 0;
}

/**
 * compute the value of a string
 * @param  dest_str
 * @param  dest_len
 * @param  md5_str
 */
int Compute_string_md5(unsigned char *dest_str, unsigned int dest_len, char *md5_str)
{
	int i;
	unsigned char md5_value[MD5_SIZE];
	MD5_CTX md5;

	// init md5
	MD5Init(&md5);

	MD5Update(&md5, dest_str, dest_len);

	MD5Final(&md5, md5_value);

	// convert md5 value to md5 string
	for(i = 0; i < MD5_SIZE; i++)
	{
		snprintf(md5_str + i*2, 2+1, "%02x", md5_value[i]);
	}

	return 0;
}

/**
 * compute the value of a file
 * @param  file_path
 * @param  md5_str
 * @return 0: ok, -1: fail
 */
int Compute_file_md5(const char *file_path, char *md5_str)
{
	int i;
	int fd;
	int ret;
	unsigned char data[READ_DATA_SIZE];
	unsigned char md5_value[MD5_SIZE];
	MD5_CTX md5;

	fd = open(file_path, O_RDONLY);
	if (-1 == fd)
	{
		perror("open");
		return -1;
	}

	// init md5
	MD5Init(&md5);

	while (1)
	{
		ret = read(fd, data, READ_DATA_SIZE);
		if (-1 == ret)
		{
			perror("read");
			close(fd);
			return -1;
		}

		MD5Update(&md5, data, ret);

		if (0 == ret || ret < READ_DATA_SIZE)
		{
			break;
		}
	}

	close(fd);

	MD5Final(&md5, md5_value);

	// convert md5 value to md5 string
	for(i = 0; i < MD5_SIZE; i++)
	{
		snprintf(md5_str + i*2, 2+1, "%02x", md5_value[i]);
	}

	return 0;
}

